package zcw.com.lib_leet_code.util;

import java.io.BufferedReader;
import java.io.Closeable;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayDeque;
import java.util.List;
import java.util.Queue;

import zcw.com.lib_leet_code.bean.ListNode;
import zcw.com.lib_leet_code.bean.Node;
import zcw.com.lib_leet_code.bean.TreeNode;

/**
 * Created by 朱城委 on 2019/6/14.<br><br>
 *
 * 工具类
 */
public class Util {

    /**
     * 创建链表
     * @param array
     * @return
     */
    public static ListNode createList(int[] array) {

        if(array == null || array.length == 0) {
            return null;
        }

        ListNode head = new ListNode(array[0]);
        ListNode indexNode = head;

        for(int i = 1; i < array.length; i++) {
            ListNode node = new ListNode(array[i]);
            indexNode.next = node;
            indexNode = indexNode.next;
        }

        return head;
    }

    /**
     * 打印链表
     * @param head
     */
    public static void printList(ListNode head) {
        while (head != null) {
            System.out.print(head.val + "\t");

            head = head.next;
        }
        System.out.println();
    }

    /**
     * 链表相连
     *
     * @param head 相连链表前段
     * @param add 相连链表后段
     * @return 返回相连后的链表
     */
    public static ListNode listAdd(ListNode head, ListNode add) {
        if(head == null) {
            return add;
        }

        ListNode curr = head;
        while (curr.next != null) {
            curr = curr.next;
        }

        curr.next = add;
        return head;
    }

    /**
     * 打印数组
     * @param array
     */
    public static void printArray(int[] array) {
        if(array == null) {
            return ;
        }

        for(int value : array) {
            System.out.print(value + "\t");
        }
        System.out.println();
    }

    /**
     * 打印数组
     * @param array
     */
    public static void printArray(String[] array) {
        if(array == null) {
            return ;
        }

        for(String value : array) {
            System.out.print(value + "\t");
        }
        System.out.println();
    }

    /**
     * 打印数组
     *
     * @param array 要打印的数组
     */
    public static void printArray(int[][] array) {
        StringBuilder builder = new StringBuilder();
        builder.append("[");

        for(int i = 0; i < array.length; i++) {
            if(array[i].length > 0) {
                builder.append("[");

                int j = 0;
                for(; j < array[i].length - 1; j++) {
                    builder.append(array[i][j]).append(",");
                }
                builder.append(array[i][j]);

                builder.append("],");
            }
        }

        // 去除多余逗号
        if(builder.length() > 1) {
            builder.setLength(builder.length() - 1);
        }

        builder.append("]");
        System.out.println(builder);
    }

    /**
     * 打印二维字符数组
     *
     * @param array 要打印的数组
     */
    public static void printArray(char[][] array) {
        if(array == null || array.length == 0) {
            return ;
        }

        for(int i = 0; i < array.length; i++) {
            for(char ch : array[i]) {
                System.out.print(String.valueOf(ch) + '\t');
            }
            System.out.println();
        }
        System.out.println();
    }

    /**
     * 利用满二叉树列表，建立树
     *
     * @param list 列表
     * @return 返回二叉树根节点
     */
    public static TreeNode createTree(List<Integer> list) {
        if(list == null || list.size() == 0) {
            return null;
        }

        TreeNode[] nodeArray = new TreeNode[list.size()];
        for(int i = 0; i < list.size(); i++) {
            if(list.get(i) != null) {
                nodeArray[i] = new TreeNode(list.get(i));
            }
        }

        int length = list.size();
        for(int i = 0; i < length / 2; i++) {
            int index = i * 2 + 1;
            if(index < length && nodeArray[i] != null && nodeArray[index] != null) {
                nodeArray[i].left = nodeArray[index];
            }

            index = (i + 1) * 2;
            if(index < length && nodeArray[i] != null && nodeArray[index] != null) {
                nodeArray[i].right = nodeArray[index];
            }
        }

        return nodeArray[0];
    }

    /**
     * 利用满二叉树列表，建立树
     *
     * @param list 列表
     * @return 返回二叉树根节点
     */
    public static Node createTree2(List<Integer> list) {
        if(list == null || list.size() == 0) {
            return null;
        }

        Node[] nodeArray = new Node[list.size()];
        for(int i = 0; i < list.size(); i++) {
            if(list.get(i) != null) {
                nodeArray[i] = new Node(list.get(i));
            }
        }

        int length = list.size();
        for(int i = 0; i < length / 2; i++) {
            int index = i * 2 + 1;
            if(index < length && nodeArray[i] != null && nodeArray[index] != null) {
                nodeArray[i].left = nodeArray[index];
            }

            index = (i + 1) * 2;
            if(index < length && nodeArray[i] != null && nodeArray[index] != null) {
                nodeArray[i].right = nodeArray[index];
            }
        }

        return nodeArray[0];
    }

    /**
     * 前序遍历二叉树
     *
     * @param root 根节点
     */
    public static void preTravelTree(TreeNode root) {
        if(root == null) {
            return ;
        }

        rawPreTravelTree(root);
        System.out.println();
    }

    /**
     * 前序遍历二叉树
     *
     * @param root 根节点
     */
    private static void rawPreTravelTree(TreeNode root) {
        if(root == null) {
            return ;
        }

        System.out.print(root.val + "\t");
        rawPreTravelTree(root.left);
        rawPreTravelTree(root.right);
    }

    /**
     * 中序遍历二叉树
     *
     * @param root 根节点
     */
    public static void inTravelTree(TreeNode root) {
        if(root == null) {
            return ;
        }

        rawInTravelTree(root);
        System.out.println();
    }

    /**
     * 中序遍历二叉树
     *
     * @param root 根节点
     */
    private static void rawInTravelTree(TreeNode root) {
        if(root == null) {
            return ;
        }

        rawInTravelTree(root.left);
        System.out.print(root.val + "\t");
        rawInTravelTree(root.right);
    }

    /**
     * 后序遍历二叉树
     *
     * @param root 根节点
     */
    public static void postTravelTree(TreeNode root) {
        if(root == null) {
            return ;
        }

        rawPostTravelTree(root);
        System.out.println();
    }

    /**
     * 后序遍历二叉树
     *
     * @param root 根节点
     */
    private static void rawPostTravelTree(TreeNode root) {
        if(root == null) {
            return ;
        }

        rawPostTravelTree(root.left);
        rawPostTravelTree(root.right);
        System.out.print(root.val + "\t");
    }

    /**
     * 层次遍历二叉树
     *
     * @param root 根节点
     */
    public static void levelTravelTree(TreeNode root) {
        if(root == null) {
            return ;
        }

        Queue<TreeNode> queue = new ArrayDeque<>();
        queue.offer(root);

        while (!queue.isEmpty()) {
            TreeNode node = queue.poll();
            System.out.print(node.val + "\t");

            if(node.left != null) queue.offer(node.left);
            if(node.right != null) queue.offer(node.right);
        }

        System.out.println();
    }

    /**
     * 获取二叉树中指定节点
     *
     * @param root 根节点
     * @param value 要获取的节点值
     * @return 如果没有此节点，返回null。
     */
    public static TreeNode getNode(TreeNode root, int value) {
        if(root == null || root.val == value) {
            return root;
        }

        TreeNode node = getNode(root.left, value);
        if(node != null) {
            return node;
        }

        return getNode(root.right, value);
    }

    /**
     * 获取链表中的指定节点
     *
     * @param head 头节点
     * @param value 要获取的节点值
     * @return 如果没有此节点，返回null。
     */
    public static ListNode getNode(ListNode head, int value) {
        while (head != null) {
            if(head.val == value) {
                return head;
            }

            head = head.next;
        }

        return null;
    }

    /**
     * 计算程序运行时间
     *
     * @param message 要显示的信息
     * @param obj 要计算的对象
     * @return 返回代理对象
     */
    public static Object calculateTime(String message, Object obj) {
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                long startNano = System.nanoTime();
                Object result = method.invoke(obj, args);
                long timeNano = System.nanoTime() - startNano;

                System.out.println(message + "，程序运行花费：" + timeNano / (1000 * 1000)  + "毫秒；" + timeNano / 1000 + "微妙");
                return result;
            }
        });
    }

    /**
     * 从文件中读取以逗号分隔的数组
     *
     * @param filePath 文件路径
     * @return 返回数组，如果读取失败，返回空数组
     */
    public static int[] readArrayFromFile(String filePath) {
        int[] empty = new int[] {};
        if(filePath == null || filePath.trim().equals("")) {
            return empty;
        }

        File file = new File(filePath);
        if(!file.exists() || file.isDirectory()) {
            return empty;
        }

        BufferedReader reader = null;
        try {
            StringBuilder builder = new StringBuilder();
            reader = new BufferedReader(new FileReader(file));
            String line;
            while ((line = reader.readLine()) != null) {
                builder.append(line);
            }

            String[] array = builder.toString().split(",");
            int[] intArray = new int[array.length];
            for(int i = 0; i < array.length; i++) {
                intArray[i] = Integer.parseInt(array[i]);
            }

            return intArray;
        } catch (Exception e) {
            e.printStackTrace();
        }
        finally {
            close(reader);
        }

        return new int[] {};
    }

    /**
     * 关闭流
     *
     * @param closeable 要关闭的流
     */
    public static void close(Closeable closeable) {
        if(closeable != null) {
            try {
                closeable.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
